package controller;

import java.awt.Cursor;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.List;

import javax.swing.event.MouseInputAdapter;

import view.ChatContainer;

import model.ChatModel;
import model.HistoryEintrag;

/**
 * Implementiert die Action eines des Chats. ChatContainer koenen diesen
 * Listener mit folgendem Code verwenden:<br>
 * <blockquote><code>ChatListener cl = new ChatListener(chatModel, this);</code><br>
 * <code>addMouseInputListener(cl);</code><br>
 * <code>addMouseListener(cl);</code><br>
 * <code>addMouseMotionListener(cl);</code><br>
 * <code>addKeyListener(cl);</code></blockquote>
 * 
 * @author Kilian Telschig, Simon Wirth
 * 
 */
public class ChatListener extends MouseInputAdapter implements KeyListener {

	/** Haelt alle wichtigen Informationen um den Chat darzustellen. */
	private ChatModel chatModel;
	/** Dort wird der Chat gezeichnet. */
	private ChatContainer chatContainer;

	/**
	 * Erstellt einen ChatListener zum Steuern der Chat-Action.
	 * 
	 * @param chatModel
	 *            haelt die Daten des Chats (history, fonts...) und wird ueber
	 *            Aenderungen benachrichtigt
	 * @param chatContainer
	 *            wird nach Aenderungen am Chat gerepaintet
	 * @throws NullPointerException
	 *             falls mindestens einer der Parameter gleich null ist
	 */
	public ChatListener(ChatModel chatModel, ChatContainer chatContainer) {
		if (chatModel == null || chatContainer == null) {
			throw new NullPointerException((chatModel == null ? "Chat"
					: "ChatContainer") + " == null");
		}

		this.chatModel = chatModel;
		this.chatContainer = chatContainer;
		setBounds();

		chatContainer.addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent e) {
				setBounds();
				redraw();
			}
		});
	}

	/**
	 * Fragt den aktuellen Bereich des Chats im ChatContainer ab und passt die
	 * Shapes an.
	 */
	private void setBounds() {
		chatContainer.computeChatRect();
		// TODO shapes anpassen bla
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		int anzahl = e.getUnitsToScroll();
		// Workaround weil manchmal um 2 und manchmal um 3 gescrolled wird
		if(anzahl > 0)
			anzahl = 1;
		else
			anzahl = -1;
		
		// Nur bei Bedarf auf Scrollen reagieren
		if (chatModel.getAlleZeilen() > chatModel.getZeilenAnzahl()) {
			//Scrollbalkenindex aendern
			if(anzahl > 0)
				chatModel.setScrollBalkenIndex(1);
			else
				chatModel.setScrollBalkenIndex(-1);
			// Nicht weiter hoch als moeglich scrollen
			if (chatModel.getStartIndex() + anzahl >= 0){
				chatModel.setStartIndex(chatModel.getStartIndex() + anzahl);

			} else
				chatModel.setStartIndex(0);
//			System.out.print(chatModel.getStartIndex()+" -> ");
			// Wenn zu weit runter gescrolled wurde korrigieren
			if (chatModel.getStartIndex() + chatModel.getZeilenAnzahl() >= chatModel.getAlleZeilen()){
				chatModel.setStartIndex(chatModel.getAlleZeilen() - chatModel.getZeilenAnzahl());
				
			}
//			System.out.println(chatModel.getStartIndex());
			redraw();
		}
		
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		if (!mouseImChatBereich(e)) {
			return;
		}

		// cursor anpassen...
		if (mouseUebermEditor(e)) {
			chatContainer.setCursor(Cursor
					.getPredefinedCursor(Cursor.TEXT_CURSOR));
		} else if (mouseUeberChatHistory(e)) {
			chatContainer.setCursor(Cursor
					.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		} else {
			chatContainer.setCursor(Cursor
					.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		if (!mouseImChatBereich(e)) {
			return;
		}
		// TODO editor aktivieren/cursor-position setzen
	}

	@Override
	public void mouseDragged(MouseEvent e) {
		if (!mouseImChatBereich(e)) {
			return;
		}
		// TODO Text markieren / markierten Text verschieben
	}

	/**
	 * Ermittelt anhand des MouseEvents, ob sich die Mouse derzeit ueber dem
	 * ChatBereich befindet.
	 * 
	 * @param e
	 *            aktuell gefeuerter MouseEvent
	 * @return true gdw der Mousezeiger sich im Chatbereich befindet
	 * @see ChatContainer#computeChatRect()
	 */
	private boolean mouseImChatBereich(MouseEvent e) {
		return chatContainer.computeChatRect().contains(e.getPoint());
	}

	/**
	 * Ermittelt anhand des MouseEvents, ob sich die Mouse derzeit ueber dem
	 * EditorBereich befindet.
	 * 
	 * @param e
	 *            aktuell gefeuerter MouseEvent
	 * @return true gdw der Mousezeiger sich im Editorbereich befindet
	 * @see ChatContainer#computeChatRect()
	 * @see ChatModel#getEditorBounds(Rectangle)
	 */
	private boolean mouseUebermEditor(MouseEvent e) {
		return chatModel.getEditorBounds(chatContainer.computeChatRect())
				.contains(e.getPoint());
	}

	/**
	 * Ermittelt anhand des MouseEvents, ob sich die Mouse derzeit ueber der
	 * Chat-History-Anzeige befindet.
	 * 
	 * @param e
	 *            aktuell gefeuerter MouseEvent
	 * @return true gdw der Mousezeiger sich ueber der Chat-History-Anzeige
	 *         befindet
	 * @see ChatContainer#computeChatRect()
	 * @see ChatModel#getChatAnzeigeBounds(Rectangle)
	 */
	private boolean mouseUeberChatHistory(MouseEvent e) {
		return chatModel.getChatAnzeigeBounds(chatContainer.computeChatRect())
				.contains(e.getPoint());
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// evtl. cursor-blinken deaktivieren? - simon: wo blinkt was? oder
		// meinst du wenns dann spaeter blinkt?

		int code = e.getKeyCode();
		switch (code) {
		case KeyEvent.VK_ENTER:
			// leere Nachrichten nicht speichern
			if (chatModel.getTextZeile().size() < 1)
				break;
			String nachricht = "";
			for (Character c : chatModel.getTextZeile()) {
				nachricht += c;
			}

			chatModel.sendeAnOutputs(nachricht);
			chatModel.getTextZeile().clear();
			//TODO hier isn bug er scrolt eins zu spaet
			if (chatModel.getAlleZeilen() - chatModel.getZeilenAnzahl() > 0)
				chatModel.setStartIndex(chatModel.getAlleZeilen() - chatModel.getZeilenAnzahl());
			else
				chatModel.setStartIndex(0);
			chatModel.setCursorIndex(-chatModel.getCursorIndex());
			break;
			
		case KeyEvent.VK_BACK_SPACE:
			List<Character> l = chatModel.getTextZeile();
			if (l != null && l.size() > 0) {
				if (chatModel.getCursorIndex() > 0) {
					l.remove(chatModel.getCursorIndex() - 1);
					chatModel.setCursorIndex(-1);
				}
			}
			break;
			
		case KeyEvent.VK_DELETE:
			List<Character> l2 = chatModel.getTextZeile();
			if (l2 != null && l2.size() > 0) {
				if (chatModel.getCursorIndex() < chatModel.getTextZeile()
						.size()) {
					l2.remove(chatModel.getCursorIndex());
				}
			}
			break;
			
		case KeyEvent.VK_LEFT:
			if (chatModel.getCursorIndex() > 0)
				chatModel.setCursorIndex(-1);
			break;
			
		case KeyEvent.VK_RIGHT:
			if (chatModel.getCursorIndex() < chatModel.getTextZeile().size())
				chatModel.setCursorIndex(1);
			break;

		default:
			//  satzzeichen etc auch beachten - durch den KeyChar
			// ->undefined-check muessten jetzt alle chars gehn
			// if (code >= 48 && code <= 90 || code == KeyEvent.VK_SPACE) {
			if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED && code != 0) {
				chatModel.getTextZeile().add(chatModel.getCursorIndex(), e.getKeyChar());
				chatModel.setCursorIndex(1);
			}
			break;
		}
		redraw();
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// evtl. cursor wieder blinken lassen?
	}

	@Override
	public void keyTyped(KeyEvent e) {
		keyPressed(e);
	}
	
	/**
	 * Bewirkt das Neu-Zeichnen des Chats.
	 */
	private void redraw() {
		chatModel.setChanged();
		chatContainer.repaint();
	}
}
